package com.ssy.lingxi.pay.serviceimpl;

import CCBSign.RSASig;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ssy.lingxi.common.constant.Constants;
import com.ssy.lingxi.common.constant.order.OrderPayChannelEnum;
import com.ssy.lingxi.common.constant.order.OrderPaymentParameterEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.component.redis.service.IRedisUtils;
import com.ssy.lingxi.order.api.feign.OrderFeignService;
import com.ssy.lingxi.order.api.model.constant.OrderFeignConstant;
import com.ssy.lingxi.order.api.model.queue.OrderPaymentCallbackDTO;
import com.ssy.lingxi.order.api.model.vo.request.OrderPayChannelFeignVO;
import com.ssy.lingxi.order.api.model.vo.response.PayChannelParameterFeignDetailVO;
import com.ssy.lingxi.order.api.model.vo.response.PaymentParameterFeignDetailVO;
import com.ssy.lingxi.pay.api.model.vo.request.ccb.*;
import com.ssy.lingxi.pay.entity.CcbTranRecordDO;
import com.ssy.lingxi.pay.model.dto.*;
import com.ssy.lingxi.pay.repository.CcbTranRecordRepository;
import com.ssy.lingxi.pay.service.ICcbPayService;
import com.ssy.lingxi.pay.service.IPayCacheService;
import com.ssy.lingxi.pay.utils.CcbPayUtil;
import com.ssy.lingxi.pay.utils.HttpClientUtil;
import com.ssy.lingxi.pay.utils.MD5Util;
import com.ssy.lingxi.pay.utils.XmlUtil;
import com.ssy.lingxi.pay.utils.ccb.AESUtil;
import com.ssy.lingxi.pay.utils.ccb.DecipherUtil_Ft;
import com.ssy.lingxi.pay.utils.ccb.EncipherUtil_Ft;
import jodd.util.StringUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * @Description: 建行支付
 * @Author: HuangJieZhou
 * @Date: 2021/12/22 15:19
 * @Version: 2.0.0
 **/
@Service
public class CcbPayServiceImpl implements ICcbPayService {

    private final static Logger logger = LoggerFactory.getLogger(CcbPayServiceImpl.class);

    @Resource
    private CcbTranRecordRepository ccbTranRecordRepository;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private IPayCacheService iPayCacheService;

    @Resource
    private OrderFeignService orderFeignService;

    @Resource
    private IRedisUtils redisUtils;

    /**
     * 判断交易订单号是否存在
     * @param payOrderNum       交易订单号
     */
    @Override
    public boolean payOrderExist(String payOrderNum) {
        return ccbTranRecordRepository.findFirstByOrderIdOrderByIdDesc(payOrderNum) != null;
    }

    /**
     * 建行B2B普通商户支付
     * @param ccbPayVo 接口请求参数
     * @return 跳转建行html页面
     */
    @Override
    public String b2bPay(CcbB2bPayVo ccbPayVo) {
        Map<String, String> params = new LinkedHashMap<>();
        //商户代码(客户号)
        params.put("MERCHANTID", ccbPayVo.getMerchantId());
        //商户柜台代码
        params.put("POSID", ccbPayVo.getPosId());
        //分行代码
        params.put("BRANCHID", ccbPayVo.getBranchId());
        //订单号
        params.put("ORDERID", ccbPayVo.getOrderCode());
        //付款金额
        params.put("PAYMENT", ccbPayVo.getPayMent().toString());
        //币种
        params.put("CURCODE", CcbPayUtil.cur_code);
        //交易码
        params.put("TXCODE", CcbPayUtil.b2b_pay_tx_code);
        //备注1
        params.put("REMARK1", StringUtils.isEmpty(ccbPayVo.getRemark1()) ? "" : ccbPayVo.getRemark1());
        //备注2
        params.put("REMARK2", StringUtils.isEmpty(ccbPayVo.getRemark2()) ? "" : ccbPayVo.getRemark2());
        //订单超时时间
        if (StringUtils.isNotEmpty(ccbPayVo.getTimeout())) {
            params.put("TIMEOUT", ccbPayVo.getTimeout());
        }
        //拼接需要加入mac计算的的字符串，以前map的元素可以为空，除了TIMEOUT之外其他元素都需要参与计算
        StringBuffer sf = new StringBuffer();
        params.forEach((key, value) -> {
            if (!key.equals("TIMEOUT")) {
                if (StringUtils.isEmpty(value)) {
                    sf.append(key).append("=").append("&");
                } else {
                    sf.append(key).append("=").append(value).append("&");
                }
            }
        });
        //注意：TIMEOUT的计算只需要计算value，不需要计算key
        String urlParams = sf.toString().substring(0, sf.toString().length() - 1) + params.get("TIMEOUT");
        String mac = MD5Util.md5Str(urlParams);
        //持久化到数据库
        CcbTranRecordDO ccbTranRecordDO = new CcbTranRecordDO();
        ccbTranRecordDO.setMerchantId(ccbPayVo.getMerchantId());
        ccbTranRecordDO.setPosId(ccbPayVo.getPosId());
        ccbTranRecordDO.setBranchId(ccbPayVo.getBranchId());
        ccbTranRecordDO.setOrderId(ccbPayVo.getOrderCode());
        ccbTranRecordDO.setCurCode(CcbPayUtil.cur_code);
        ccbTranRecordDO.setTxCode(CcbPayUtil.b2b_pay_tx_code);
        ccbTranRecordDO.setRemark1(ccbPayVo.getRemark1());
        ccbTranRecordDO.setRemark2(ccbPayVo.getRemark2());
        ccbTranRecordDO.setTimeout(ccbPayVo.getTimeout());
        ccbTranRecordDO.setCreateTime(LocalDateTime.now());
        ccbTranRecordDO.setIfCallback(new Short("0"));
        ccbTranRecordDO.setAttach(ccbPayVo.getAttach());
        ccbTranRecordRepository.save(ccbTranRecordDO);

        return CcbPayUtil.b2b_pay_bank_url + "?" + urlParams + "&MAC=" + mac;
    }

    /**
     * 建行数字人民币支付(PC)
     * @param ccbDigitalPayVo 接口请求参数
     * @return 二维码路径
     */
    @Override
    public String digitalPcPay(CcbDigitalPayVo ccbDigitalPayVo) {
        Map<String, Object> params = this.getDigitalPcPayParamMap(ccbDigitalPayVo);
        try{
            //调用建行数字银行支付
            String result = HttpClientUtil.httpPost(CcbPayUtil.digital_bank_url, params);
            JSONObject jsonObject = JSONUtil.parseObj(result);
            boolean flag = jsonObject.getBool(CcbPayUtil.success);
            if(flag){
                String str = jsonObject.getStr(CcbPayUtil.pay_url);
                result = HttpClientUtil.httpPost(str);
                jsonObject = JSONUtil.parseObj(result);
                flag = jsonObject.getBool(CcbPayUtil.success);
                if(flag){
                    //持久化到数据库
                    CcbTranRecordDO ccbTranRecordDO = new CcbTranRecordDO();
                    ccbTranRecordDO.setMerchantId(ccbDigitalPayVo.getMerchantId());
                    ccbTranRecordDO.setPosId(ccbDigitalPayVo.getPosId());
                    ccbTranRecordDO.setBranchId(ccbDigitalPayVo.getBranchId());
                    ccbTranRecordDO.setOrderId(ccbDigitalPayVo.getOrderCode());
                    ccbTranRecordDO.setCurCode(CcbPayUtil.cur_code);
                    ccbTranRecordDO.setTxCode(CcbPayUtil.b2b_pay_tx_code);
                    ccbTranRecordDO.setRemark1(ccbDigitalPayVo.getRemark1());
                    ccbTranRecordDO.setRemark2(ccbDigitalPayVo.getRemark2());
                    ccbTranRecordDO.setTimeout(ccbDigitalPayVo.getTimeout());
                    ccbTranRecordDO.setCreateTime(LocalDateTime.now());
                    ccbTranRecordDO.setIfCallback(new Short("0"));
                    ccbTranRecordDO.setAttach(ccbDigitalPayVo.getAttach());
                    ccbTranRecordRepository.save(ccbTranRecordDO);
                    //返回二维码
                    return URLDecoder.decode(jsonObject.getStr(CcbPayUtil.qr_url), CharsetUtil.UTF_8);
                }else{
                    logger.info("建行数字人民币支付(PC)====第二次请求====" + jsonObject.toString());
                    throw new BusinessException(ResponseCode.PAY_CCB_GATEWAY_EXCEPTION);
                }
            }else{
                logger.info("建行数字人民币支付(PC)====第一次请求====" + jsonObject.toString());
                throw new BusinessException(ResponseCode.PAY_CCB_GATEWAY_EXCEPTION);
            }
        }catch (Exception e){
            throw new BusinessException(ResponseCode.PAY_CCB_GATEWAY_EXCEPTION);
        }
    }

    /**
     * 组装数字人民币请求参数
     * @param ccbDigitalVo      参数
     */
    private Map<String, Object> getDigitalPcPayParamMap(CcbDigitalPayVo ccbDigitalVo){
        //注：map的顺序不能乱
        Map<String, Object> params = new LinkedHashMap<>();
        //商户代码
        params.put("MERCHANTID", ccbDigitalVo.getMerchantId());
        //柜台代号
        params.put("POSID", ccbDigitalVo.getPosId());
        //分行代码
        params.put("BRANCHID", ccbDigitalVo.getBranchId());
        //二级商户代码
        params.put("SUB_MERCHANTID", ccbDigitalVo.getSubMerchantId());
        //订单号
        params.put("ORDERID", ccbDigitalVo.getOrderCode());
        //付款金额
        params.put("PAYMENT", ccbDigitalVo.getPayMent().toString());
        //商户结算账号
        params.put("CdtrWltId", ccbDigitalVo.getCdtrWltId());
        //币种
        params.put("CURCODE", CcbPayUtil.cur_code);
        //交易码
        params.put("TXCODE", CcbPayUtil.digital_pc_pay_tx_code);
        try{
            ScriptEngineManager sem = new ScriptEngineManager();
            ScriptEngine engine = sem.getEngineByExtension("js");
            //备注1
            params.put("REMARK1", StringUtils.isEmpty(ccbDigitalVo.getRemark1()) ? "" : engine.eval(" escape('" + ccbDigitalVo.getRemark1() + "')"));
            //备注2
            params.put("REMARK2", StringUtils.isEmpty(ccbDigitalVo.getRemark2()) ? "" : engine.eval(" escape('" + ccbDigitalVo.getRemark2() + "')"));
        }catch (Exception e){
            throw new BusinessException(ResponseCode.PAY_CCB_REMARK_EXCEPTION);
        }
        //返回类型
        params.put("RETURNTYPE", CcbPayUtil.return_type);
        //订单超时时间
        params.put("TIMEOUT", ccbDigitalVo.getTimeout() == null ? "" : ccbDigitalVo.getTimeout());
        //柜台的公钥后30位
        params.put("PUB", ccbDigitalVo.getPublicKey().substring(ccbDigitalVo.getPublicKey().length() - 30));
        //MAC校验域
        String mac = MapUtil.join(params, "&", "=", false);
        params.put("MAC", MD5Util.md5Str(mac));

        return params;
    }

    /**
     * 建行数字人民币支付(APP)
     * @param ccbDigitalPayVo 接口请求参数
     * @return h5页面
     */
    @Override
    public String digitalAppPay(CcbDigitalPayVo ccbDigitalPayVo) {
        Map<String, Object> params = this.getDigitalAppPayParamMap(ccbDigitalPayVo);
        params.put("Mrch_url", ccbDigitalPayVo.getMrchUrl());
        //持久化到数据库
        CcbTranRecordDO ccbTranRecordDO = new CcbTranRecordDO();
        ccbTranRecordDO.setMerchantId(ccbDigitalPayVo.getMerchantId());
        ccbTranRecordDO.setPosId(ccbDigitalPayVo.getPosId());
        ccbTranRecordDO.setBranchId(ccbDigitalPayVo.getBranchId());
        ccbTranRecordDO.setOrderId(ccbDigitalPayVo.getOrderCode());
        ccbTranRecordDO.setCurCode(CcbPayUtil.cur_code);
        ccbTranRecordDO.setTxCode(CcbPayUtil.b2b_pay_tx_code);
        ccbTranRecordDO.setRemark1(ccbDigitalPayVo.getRemark1());
        ccbTranRecordDO.setRemark2(ccbDigitalPayVo.getRemark2());
        ccbTranRecordDO.setTimeout(ccbDigitalPayVo.getTimeout());
        ccbTranRecordDO.setCreateTime(LocalDateTime.now());
        ccbTranRecordDO.setIfCallback(new Short("0"));
        ccbTranRecordDO.setAttach(ccbDigitalPayVo.getAttach());
        ccbTranRecordRepository.save(ccbTranRecordDO);

        //调用建行数字银行支付
        return CcbPayUtil.digital_bank_url + "&" + MapUtil.join(params, "&", "=", false);
    }

    /**
     * 组装数字人民币请求参数
     * @param ccbDigitalVo      参数
     */
    private Map<String, Object> getDigitalAppPayParamMap(CcbDigitalPayVo ccbDigitalVo){
        //注：map的顺序不能乱
        Map<String, Object> params = new LinkedHashMap<>();
        //商户代码
        params.put("MERCHANTID", ccbDigitalVo.getMerchantId());
        //柜台代号
        params.put("POSID", ccbDigitalVo.getPosId());
        //分行代码
        params.put("BRANCHID", ccbDigitalVo.getBranchId());
        //二级商户代码
        if(StringUtil.isNotEmpty(ccbDigitalVo.getSubMerchantId())){
            params.put("SUB_MERCHANTID", ccbDigitalVo.getSubMerchantId());
        }
        //订单号
        params.put("ORDERID", ccbDigitalVo.getOrderCode());
        //付款金额
        params.put("PAYMENT", ccbDigitalVo.getPayMent().toString());
        //商户结算账号
        if(StringUtil.isNotEmpty(ccbDigitalVo.getCdtrWltId())){
            params.put("CdtrWltId", ccbDigitalVo.getCdtrWltId());
        }
        //币种
        params.put("CURCODE", CcbPayUtil.cur_code);
        //交易码
        params.put("TXCODE", CcbPayUtil.digital_app_pay_tx_code);
        try{
            ScriptEngineManager sem = new ScriptEngineManager();
            ScriptEngine engine = sem.getEngineByExtension("js");
            //备注1
            params.put("REMARK1", StringUtils.isEmpty(ccbDigitalVo.getRemark1()) ? "" : engine.eval(" escape('" + ccbDigitalVo.getRemark1() + "')"));
            //备注2
            params.put("REMARK2", StringUtils.isEmpty(ccbDigitalVo.getRemark2()) ? "" : engine.eval(" escape('" + ccbDigitalVo.getRemark2() + "')"));
        }catch (Exception e){
            throw new BusinessException(ResponseCode.PAY_CCB_REMARK_EXCEPTION);
        }
        //返回类型
        params.put("RETURNTYPE", CcbPayUtil.return_type);
        //订单超时时间
        params.put("TIMEOUT", ccbDigitalVo.getTimeout() == null ? "" : ccbDigitalVo.getTimeout());
        //柜台的公钥后30位
        params.put("PUB", ccbDigitalVo.getPublicKey().substring(ccbDigitalVo.getPublicKey().length() - 30));
        //MAC校验域
        String mac = MapUtil.join(params, "&", "=", false);
        params.put("MAC", MD5Util.md5Str(mac));

        return params;
    }

    /**
     * 建行数字人民币支付结果查询
     * @param ccbDigitalPayQueryVo 接口请求参数
     * @return 支付结果，枚举类：CcbDigitalPayResultEnum
     */
    @Override
    public String getDigitalPayResult(CcbDigitalPayQueryVo ccbDigitalPayQueryVo) {
        return null;
    }

    /**
     * 建行数字人民币退款
     * @param digitalRefundVO 接口请求参数
     * @return 成功返回true, 失败直接抛异常
     */
    @Override
    public boolean digitalRefund(DigitalRefundVO digitalRefundVO) {
        //获取缓存的信息
        CcbRefundCacheDTO ccbRefundCacheDTO = this.getAuthAddress();
        String corpId = ccbRefundCacheDTO.getCorpId();
        String scenario = ccbRefundCacheDTO.getScenario();
        String customerId = ccbRefundCacheDTO.getCustomerId();
        String userId = ccbRefundCacheDTO.getUserId();
        String password = ccbRefundCacheDTO.getPassword();
        String req_url = ccbRefundCacheDTO.getReqUrl();
        String access_token = ccbRefundCacheDTO.getAccessToken();
        String communication_key = ccbRefundCacheDTO.getCommunicationKey();

        //注：map的顺序不能乱
        Map<String, Object> params = new LinkedHashMap<>();
        //调用方ID
        params.put("FT_CORPID", corpId);
        //接口Id
        params.put("FT_TILEID", CcbPayUtil.digital_refund_ft_tileId);
        //接口使用场景
        params.put("FT_SCENARIO", scenario);
        //瓦片流水号
        params.put("FT_ORDERNO", UUID.randomUUID().toString().replace("-",""));
        //商户代码
        params.put("CUSTOMERID", customerId);
        //操作员号
        params.put("USERID", userId);
        //密码
        params.put("PASSWORD", password);
        //交易码
        params.put("TXCODE", CcbPayUtil.digital_refund_tx_code);
        //语言
        params.put("LANGUAGE", CcbPayUtil.language);
        //版本
        params.put("CCB_IBSVersion", CcbPayUtil.version);
        //固定值
        params.put("PT_STYLE", CcbPayUtil.pt_style);
        //固定值
        params.put("resType", CcbPayUtil.res_type);

        //退款金额
        params.put("MONEY", digitalRefundVO.getMoney().toString());
        //订单开始时间
        params.put("Enqr_StDt", digitalRefundVO.getOrderStartTime());
        //订单结束时间
        params.put("Enqr_CODt", digitalRefundVO.getOrderEndTime());
        //退款流水号
        params.put("MsgRp_Jrnl_No", digitalRefundVO.getRefundSn());
        //订单号
        params.put("ORDER", digitalRefundVO.getOrderCode());
        String ccbParam = MapUtil.join(params, "&", "=", false);

        try{
            ccbParam = AESUtil.encrypt(ccbParam, communication_key);
            ccbParam = URLEncoder.encode(ccbParam, CharsetUtil.UTF_8);
            String body = HttpClientUtil.httpPostDigital(req_url, "FT_CORPID=" + corpId + "&ccbParam=" + ccbParam + "&ACCESS_TOKEN=" + access_token);
            String result = AESUtil.decrypt(body, communication_key);
            CcbAuthAddressDTO ccbAddressDTO = JSONUtil.toBean(result, CcbAuthAddressDTO.class);
            if(CcbPayUtil.success_code.equals(ccbAddressDTO.getRETURN_CODE())) {
                return true;
            }else{
                logger.error("商户单笔退款交易失败===={}", result);
                throw new BusinessException(ccbAddressDTO.getRETURN_MSG());
            }
        }catch (Exception e){
            e.printStackTrace();
            throw new BusinessException(ResponseCode.PAY_CCB_TRADE_REFUND_EXCEPTION);
        }
    }

    /**
     * 获取授权和通道寻址(授权交易调用和调用方金融通道寻址)
     */
    private CcbRefundCacheDTO getAuthAddress(){
        Object obj = redisUtils.get(Constants.REDIS_KEY_CCB_PAY_AUTH, Constants.REDIS_PAY_INDEX);
        if (obj != null) {
            return JSONUtil.parse(obj).toBean(CcbRefundCacheDTO.class);
        } else {
            String body = "";
            String corpId = "";
            String scenario = "";
            String customerId = "";
            String userId = "";
            String password = "";
            String pubKey1 = "";
            String pubKey2 = "";
            String priKey1 = "";
            //从订单服务查询支付参数
            OrderPayChannelFeignVO orderPayChannelFeignVO = new OrderPayChannelFeignVO();
            orderPayChannelFeignVO.setPayChannel(OrderPayChannelEnum.CCB_DIGITAL);
            Wrapper<PaymentParameterFeignDetailVO> parameterResult = orderFeignService.findPlatformPaymentParameters(orderPayChannelFeignVO);
            if (parameterResult.getCode() != ResponseCode.SUCCESS.getCode()) {
                throw new BusinessException(parameterResult.getCode(), parameterResult.getMessage());
            }
            if (CollectionUtils.isEmpty(parameterResult.getData().getParameters())) {
                throw new BusinessException(ResponseCode.PAY_CCB_DIGITAL_PARAM_NOT_EXIST);
            }
            List<PayChannelParameterFeignDetailVO> parameters = parameterResult.getData().getParameters();
            for (PayChannelParameterFeignDetailVO p : parameters) {
                if (p.getParameter().equals(OrderPaymentParameterEnum.CCB_DIGITAL_CORP_ID)) {
                    corpId = p.getValue();
                } else if (p.getParameter().equals(OrderPaymentParameterEnum.CCB_DIGITAL_SCENARIO)) {
                    scenario = p.getValue();
                }else if (p.getParameter().equals(OrderPaymentParameterEnum.CCB_DIGITAL_MERCHANT_ID)) {
                    customerId = p.getValue();
                }else if (p.getParameter().equals(OrderPaymentParameterEnum.CCB_DIGITAL_USER_ID)) {
                    userId = p.getValue();
                }else if (p.getParameter().equals(OrderPaymentParameterEnum.CCB_DIGITAL_PASSWORD)) {
                    password = p.getValue();
                }else if (p.getParameter().equals(OrderPaymentParameterEnum.CCB_DIGITAL_PUB_KEY_1)) {
                    pubKey1 = p.getValue();
                }else if (p.getParameter().equals(OrderPaymentParameterEnum.CCB_DIGITAL_PUB_KEY_2)) {
                    pubKey2 = p.getValue();
                }else if (p.getParameter().equals(OrderPaymentParameterEnum.CCB_DIGITAL_PRI_KEY_1)) {
                    priKey1 = p.getValue();
                }
            }
            if(StringUtil.isEmpty(corpId)){
                throw new BusinessException(OrderPaymentParameterEnum.CCB_DIGITAL_CORP_ID.getKey() + ResponseCode.PARAM_NOT_EXIST.getMessage());
            }
            if(StringUtil.isEmpty(scenario)){
                throw new BusinessException(OrderPaymentParameterEnum.CCB_DIGITAL_SCENARIO.getKey() + ResponseCode.PARAM_NOT_EXIST.getMessage());
            }
            if(StringUtil.isEmpty(customerId)){
                throw new BusinessException(OrderPaymentParameterEnum.CCB_DIGITAL_MERCHANT_ID.getKey() + ResponseCode.PARAM_NOT_EXIST.getMessage());
            }
            if(StringUtil.isEmpty(userId)){
                throw new BusinessException(OrderPaymentParameterEnum.CCB_DIGITAL_USER_ID.getKey() + ResponseCode.PARAM_NOT_EXIST.getMessage());
            }
            if(StringUtil.isEmpty(password)){
                throw new BusinessException(OrderPaymentParameterEnum.CCB_DIGITAL_PASSWORD.getKey() + ResponseCode.PARAM_NOT_EXIST.getMessage());
            }
            if(StringUtil.isEmpty(pubKey1)){
                throw new BusinessException(OrderPaymentParameterEnum.CCB_DIGITAL_PUB_KEY_1.getKey() + ResponseCode.PARAM_NOT_EXIST.getMessage());
            }
            if(StringUtil.isEmpty(pubKey2)){
                throw new BusinessException(OrderPaymentParameterEnum.CCB_DIGITAL_PUB_KEY_2.getKey() + ResponseCode.PARAM_NOT_EXIST.getMessage());
            }
            if(StringUtil.isEmpty(priKey1)){
                throw new BusinessException(OrderPaymentParameterEnum.CCB_DIGITAL_PRI_KEY_1.getKey() + ResponseCode.PARAM_NOT_EXIST.getMessage());
            }

            try{
                //获取参数
                String data = "FT_CORPID=" + corpId + "&FT_TILEID=" + CcbPayUtil.digital_refund_ft_tileId + "&FT_SCENARIO=" + scenario;
                String ccbParam = URLEncoder.encode(EncipherUtil_Ft.encipherWithRSASignandAES(data, pubKey1.substring(pubKey1.length() - 32), priKey1), CharsetUtil.UTF_8);
                String param = "FT_CORPID=" + corpId + "&ccbParam=" + ccbParam;
                //授权交易调用
                body = HttpClientUtil.httpPostDigital(CcbPayUtil.auth_trade_url, param);
            }catch (Exception e){
                e.printStackTrace();
                logger.error("授权交易调用出现异常，body===={}", body);
                throw new BusinessException(ResponseCode.PAY_CCB_TRADE_AUTH_EXCEPTION);
            }
            CcbAuthDTO ccbAuthDTO = JSONUtil.toBean(body, CcbAuthDTO.class);
            Boolean succeed = ccbAuthDTO.getSUCCEED();
            if(succeed){
                //调用方金融通道寻址
                String acc_entry = ccbAuthDTO.getACC_ENTRY();
                String access_token = ccbAuthDTO.getACCESS_TOKEN();
                String sn = UUID.randomUUID().toString().replace("-","");
                try{
                    String communication_key = JSONUtil.parseObj(DecipherUtil_Ft.decipherWithRSASignandAES(ccbAuthDTO.getCOMMUNICATION_PWD(), pubKey2)).getStr(CcbPayUtil.communication_key);
                    String ccbParam = AESUtil.encrypt("FT_CORPID=" + corpId + "&FT_TILEID=" + CcbPayUtil.digital_refund_ft_tileId + "&FT_SCENARIO=" + scenario + "&FT_ORDERNO=" + sn + "&CUSTOMERID=" + customerId + "&USERID=" + userId + "&PASSWORD=" + password + "&TXCODE=" + CcbPayUtil.digital_trade_auth_tx_code + "&LANGUAGE=" + CcbPayUtil.language + "&CCB_IBSVersion=" + CcbPayUtil.version + "&PT_STYLE=" + CcbPayUtil.pt_style + "&resType=" + CcbPayUtil.res_type, communication_key);
                    ccbParam = URLEncoder.encode(ccbParam, CharsetUtil.UTF_8);
                    access_token = URLEncoder.encode(access_token, CharsetUtil.UTF_8);

                    body = HttpClientUtil.httpPostDigital(acc_entry, "FT_CORPID=" + corpId + "&ccbParam=" + ccbParam + "&ACCESS_TOKEN=" + access_token);
                    String result = AESUtil.decrypt(body, communication_key);
                    CcbAuthAddressDTO ccbAddressDTO = JSONUtil.toBean(result, CcbAuthAddressDTO.class);
                    if(CcbPayUtil.success_code.equals(ccbAddressDTO.getRETURN_CODE())) {
                        String req_url = ccbAddressDTO.getDataInfo().getREQ_URL();
                        //缓存数据
                        CcbRefundCacheDTO ccbRefundCacheDTO = new CcbRefundCacheDTO();
                        ccbRefundCacheDTO.setReqUrl(req_url);
                        ccbRefundCacheDTO.setAccessToken(access_token);
                        ccbRefundCacheDTO.setCommunicationKey(communication_key);
                        ccbRefundCacheDTO.setCorpId(corpId);
                        ccbRefundCacheDTO.setScenario(scenario);
                        ccbRefundCacheDTO.setCustomerId(customerId);
                        ccbRefundCacheDTO.setUserId(userId);
                        ccbRefundCacheDTO.setPassword(password);
                        //缓存时间为1天
                        redisUtils.set(Constants.REDIS_KEY_CCB_PAY_AUTH, ccbRefundCacheDTO, Constants.DAY_TO_SECONDS, Constants.REDIS_PAY_INDEX);
                        return ccbRefundCacheDTO;
                    }else{
                        logger.error("调用方金融通道寻址失败===={}", result);
                        throw new BusinessException(ccbAddressDTO.getRETURN_MSG());
                    }
                }catch (Exception e){
                    e.printStackTrace();
                    logger.error("调用方金融通道寻址出现异常，body======{}" + body);
                    throw new BusinessException(ResponseCode.PAY_CCB_TRADE_ADDRESS_EXCEPTION);
                }
            }else{
                logger.error("授权交易调用失败，body===={}", body);
                throw new BusinessException(ccbAuthDTO.getERROR_MSG());
            }
        }
    }

    /**
     * 建行B2B订单支付状态查询
     * @param ccbAccountQueryVo 请求参数
     * @return 订单状态，枚举类型: CcbB2bPayResultEnum
     */
    @Override
    public String b2bPayStatusQuery(CcbB2bPayQueryVo ccbAccountQueryVo) {
        //注：map的顺序不能乱
        Map<String, Object> map = new LinkedHashMap<>();
        //商户代码
        map.put("MERCHANTID", ccbAccountQueryVo.getMerchantId());
        //分行代码
        map.put("BRANCHID", ccbAccountQueryVo.getBranchId());
        //柜台号
        map.put("POSID", ccbAccountQueryVo.getPosId());
        //订单日期
        map.put("ORDERDATE", "");
        //订单开始时间
        map.put("BEGORDERTIME", "");
        //订单截止时间
        map.put("ENDORDERTIME", "");
        //订单号
        map.put("ORDERID", ccbAccountQueryVo.getOrderCode());
        //查询密码
        map.put("QUPWD", "");
        //交易码
        map.put("TXCODE", CcbPayUtil.b2b_query_tx_code);
        //流水类型 0支付流水 1退款流水
        map.put("TYPE", "0");
        //流水状态 0 未结算流水 1 已结算流水
        map.put("KIND", "0");
        //交易状态 0失败 1成功 2不确定 3全部（已结算流水查询不支持全部）
        map.put("STATUS", "3");
        //查询方式 (1.页面形式 2.文件返回形式 (提供TXT和XML格式文件的下载) 3.XML页面形式 )
        map.put("SEL_TYPE", "3");
        //页码
        map.put("PAGE", "1");
        //操作员
        map.put("OPERATOR", "");
        //预留字段
        map.put("CHANNEL", "");
        //校验信息
        String mac = MapUtil.join(map, "&", "=", false);
        map.put("MAC", MD5Util.md5Str(mac));
        //查询密码(值不参与mac校验)
        map.put("QUPWD", ccbAccountQueryVo.getPassword());
        String ret = HttpClientUtil.httpPost(CcbPayUtil.merchant_batch_bank_url, map);
        if(StringUtil.isNotEmpty(ret)){
            logger.info("查询返回的数据:{}", ret.replaceAll("\n", ""));
            JSONObject jsonObject;
            try {
                //将返回的xml字符串转成json对象
                jsonObject = XmlUtil.xml2Json(ret.replaceAll("<\\?.*\\?>", ""), CharsetUtil.UTF_8);
            }catch (Exception e){
                logger.error("建行B2B订单支付状态查询, 数据解析出现异常,{}", e.getMessage());
                throw new BusinessException("建行B2B订单支付状态查询, 数据解析出现异常");
            }
            if(jsonObject != null){
                CcbQueryRootDTO ccbQueryRootDTO = iPayCacheService.deserializeObject(jsonObject.toString(), CcbQueryRootDTO.class);
                CcbDocumentDTO document = ccbQueryRootDTO.getDocument();
                //根据订单号查询是否存在该流水记录，如果返回的RETURN_CODE不为000000
                if (CcbPayUtil.success_code.equals(document.getReturnCode())) {
                    List<CcbQueryOrderDTO> queryOrder = document.getQueryOrder();
                    return queryOrder.get(0).getStatusCode();
                }else{
                    throw new BusinessException(document.getReturnMsg());
                }
            }else{
                logger.info("建行B2B订单支付状态查询, 数据解析失败");
            }
        }else{
            logger.info("建行B2B订单支付状态查询返回的数据为空");
        }
        return null;
    }

    /**
     * 建行B2B普通商户支付回调
     * @param map 建行回调参数
     * @return 验签结果
     */
    @Override
    public boolean b2bPayNotify(Map<String, String> map) {
        //获取数字签名加密串
        String sign = map.get("SIGNSTRING");
        //需要将账户名称、分行名称转成GBK的字符串
        String accName = map.get("ACC_NAME");
        String branchName = map.get("BRANCH_NAME");
        //拼接参与验签的字符串原串，不含字段名称，按接口字段次序拼接值
        StringBuilder sb = new StringBuilder();
        sb.append(map.get("MPOSID"))
                .append(map.get("ORDER_NUMBER"))
                .append(map.get("CUST_ID"))
                .append(map.get("ACC_NO"))
                .append(accName)
                .append(map.get("AMOUNT"))
                .append(map.get("STATUS"));
        if (StringUtils.isNotEmpty(map.get("REMARK1"))) {
            sb.append(map.get("REMARK1"));
        }
        if (StringUtils.isNotEmpty(map.get("REMARK2"))) {
            sb.append(map.get("REMARK2"));
        }
        sb.append(map.get("TRAN_FLAG"))
                .append(map.get("TRAN_TIME"))
                .append(branchName);
        logger.info("参与验签的串:{}", sb.toString());
        //从订单服务查询支付参数
        boolean flag = false;
        String publicKey = this.getPublicKey(1);
        if(StringUtils.isNotEmpty(publicKey)){
            //String publicKey = "30819c300d06092a864886f70d010101050003818a0030818602818065c48c70da2c150c7f493119f94a4cd911feff3f5490f4a7be483a0f13ffa6d25feac24f8fba440701edd0313a66a67008747f7a3985e137b8f20ebc97352cc535ea296f050e3f77349373dba80e83cbcb787e276b285b6a6819ff2878cd7f062cb152aec615e5055a56d37647a7e5aa55d4e38fd19c8ff4454bb5b63235286f020111";
            RSASig rsaSig = new RSASig();
            rsaSig.setPublicKey(publicKey);
            flag = rsaSig.verifySigature(sign, sb.toString());
            //验签成功做数据持久化处理及通知订单业务支付的状态
            if (flag) {
                //根据支付订单号查询订单记录，更新回调的参数
                String orderId = map.get("ORDER_NUMBER");
                CcbTranRecordDO ccbTranRecordDO = ccbTranRecordRepository.findFirstByOrderIdOrderByIdDesc(orderId);
                if(ccbTranRecordDO != null){
                    ccbTranRecordDO.setCustId(map.get("CUST_ID"));
                    ccbTranRecordDO.setAccNo(map.get("ACC_NO"));
                    ccbTranRecordDO.setStatus(map.get("STATUS"));
                    ccbTranRecordDO.setTranFlag(map.get("TRAN_FLAG"));
                    ccbTranRecordDO.setTranTime(map.get("TRAN_TIME"));
                    ccbTranRecordDO.setBranchName(map.get("BRANCH_NAME"));
                    ccbTranRecordDO.setIfCallback(new Short("1"));
                    ccbTranRecordRepository.save(ccbTranRecordDO);
                    //mq通知业务系统
                    OrderPaymentCallbackDTO orderPaymentCallbackDTO = new OrderPaymentCallbackDTO();
                    orderPaymentCallbackDTO.setAttach(ccbTranRecordDO.getAttach());
                    orderPaymentCallbackDTO.setTradeNo(ccbTranRecordDO.getOrderId());
                    switch (map.get("STATUS")) {
                        case "2":
                            orderPaymentCallbackDTO.setPayStatus(1);
                            break;
                        case "5":
                            orderPaymentCallbackDTO.setPayStatus(2);
                            break;
                        case "6":
                            orderPaymentCallbackDTO.setPayStatus(0);
                            break;
                    }
                    //发送通知业务系统的mq
                    logger.info("建行B2B普通商户支付支付回调订单状态确认，发送mq通知订单服务内容:{}", iPayCacheService.serializeObject(orderPaymentCallbackDTO));
                    rabbitTemplate.convertAndSend(OrderFeignConstant.ORDER_PAY_CALLBACK_EXCHANGE,
                            OrderFeignConstant.ORDER_PAY_CALLBACK_ROUTINGKEY,
                            iPayCacheService.serializeObject(orderPaymentCallbackDTO));
                }else{
                    logger.error("订单号：" + orderId + "的支付记录不存在");
                }
            }else{
                logger.error("建行b2b支付验签失败");
            }
        }else{
            logger.error("获取建行b2b支付公钥失败");
        }
        return flag;
    }

    /**
     * 建行数字人民币支付回调
     * @param map 建行回调参数
     * @return 验签结果
     */
    @Override
    public boolean digitalPayNotify(Map<String, String> map) {
        Map<String, String> paramMap = new LinkedHashMap<>();
        paramMap.put("POSID", map.get("POSID"));
        paramMap.put("BRANCHID", map.get("BRANCHID"));
        paramMap.put("ORDERID", map.get("ORDERID"));
        paramMap.put("PAYMENT", map.get("PAYMENT"));
        paramMap.put("CURCODE", map.get("CURCODE"));
        paramMap.put("REMARK1", map.get("REMARK1"));
        paramMap.put("REMARK2", map.get("REMARK2"));
        paramMap.put("ACC_TYPE", map.get("ACC_TYPE"));
        paramMap.put("SUCCESS", map.get("SUCCESS"));
        if(map.get("TYPE") != null){
            paramMap.put("TYPE", map.get("TYPE"));
        }
        if(map.get("REFERER") != null){
            paramMap.put("REFERER", map.get("REFERER"));
        }
        if(map.get("CLIENTIP") != null){
            paramMap.put("CLIENTIP", map.get("CLIENTIP"));
        }
        if(map.get("ACCDATE") != null){
            paramMap.put("ACCDATE", map.get("ACCDATE"));
        }
        String str = MapUtil.join(paramMap, "&", "=", false);
        logger.info("参与验签的串:{}", str);
        //从订单服务查询支付参数
        boolean flag = false;
        String publicKey = this.getPublicKey(2);
        if(StringUtils.isNotEmpty(publicKey)){
            //根据订单号查找验签的公钥
//            String publicKey = "30819d300d06092a864886f70d010101050003818b0030818702818100843d79ede64a1b654e574f5d7f96fdcb98794e690f36d35276a26e06fe6b051fca766f29bea5499d8833b0cc4766681d0d866a0ff2dae8b87779a13dff95b9124e072ba41e3d645a8999a477ab9541411394b50d16fc8c5d2faae1985dfcee541512575dcb9f186d78736c3bc8ecca50d611a604b9cd873689900d284f4b4c83020111";
            RSASig rsaSig = new RSASig();
            rsaSig.setPublicKey(publicKey);
            flag = rsaSig.verifySigature(map.get("SIGN"), str);
            //验签成功做数据持久化处理及通知订单业务支付的状态
            if (flag) {
                //根据支付订单号查询订单记录，更新回调的参数
                String orderId = map.get("ORDERID");
                CcbTranRecordDO ccbTranRecordDO = ccbTranRecordRepository.findFirstByOrderIdOrderByIdDesc(orderId);
                if(ccbTranRecordDO != null){
                    ccbTranRecordDO.setTranTime(map.get("ACCDATE"));
                    ccbTranRecordDO.setIfCallback(new Short("1"));
                    ccbTranRecordRepository.save(ccbTranRecordDO);

                    //mq通知业务系统
                    OrderPaymentCallbackDTO orderPaymentCallbackDTO = new OrderPaymentCallbackDTO();
                    orderPaymentCallbackDTO.setAttach(ccbTranRecordDO.getAttach());
                    orderPaymentCallbackDTO.setTradeNo(ccbTranRecordDO.getOrderId());
                    switch (map.get("SUCCESS")) {
                        case "Y":
                            orderPaymentCallbackDTO.setPayStatus(1);
                            break;
                        case "N":
                            orderPaymentCallbackDTO.setPayStatus(2);
                            break;
                    }
                    //发送通知业务系统的mq
                    logger.info("建行数字人民币支付回调订单状态确认，发送mq通知订单服务内容:{}", iPayCacheService.serializeObject(orderPaymentCallbackDTO));
                    rabbitTemplate.convertAndSend(OrderFeignConstant.ORDER_PAY_CALLBACK_EXCHANGE,
                            OrderFeignConstant.ORDER_PAY_CALLBACK_ROUTINGKEY,
                            iPayCacheService.serializeObject(orderPaymentCallbackDTO));
                }else{
                    logger.error("订单号：" + orderId + "的支付记录不存在");
                }
            }else{
                logger.error("建行数字人民币验签失败, 验签结果:{}", false);
            }
        }else{
            logger.error("获取建行数字人民币公钥失败");
        }
        return flag;
    }

    /**
     * 通过订单服务查询支付公钥
     * @param payType       1-建行b2b支付; 2-建行数字人民币支付;
     */
    private String getPublicKey(Integer payType){
        //从订单服务查询支付参数
        OrderPayChannelFeignVO orderPayChannelFeignVO = new OrderPayChannelFeignVO();
        orderPayChannelFeignVO.setPayChannel(payType == 1 ? OrderPayChannelEnum.CCB_B2B : OrderPayChannelEnum.CCB_DIGITAL);
        Wrapper<PaymentParameterFeignDetailVO> parameterResult = orderFeignService.findPlatformPaymentParameters(orderPayChannelFeignVO);
        if (parameterResult.getCode() == ResponseCode.SUCCESS.getCode()) {
            if (!CollectionUtils.isEmpty(parameterResult.getData().getParameters())) {
                String publicKey = null;
                List<PayChannelParameterFeignDetailVO> parameters = parameterResult.getData().getParameters();
                for (PayChannelParameterFeignDetailVO p : parameters) {
                    if (p.getParameter().equals(payType == 1 ? OrderPaymentParameterEnum.CCB_B2B_PUBLIC_KEY : OrderPaymentParameterEnum.CCB_DIGITAL_PUBLIC_KEY)) {
                        publicKey = p.getValue();
                    }
                }
                if(StringUtils.isNotEmpty(publicKey)){
                    return publicKey;
                }else{
                    throw new BusinessException(payType == 1 ? OrderPaymentParameterEnum.CCB_B2B_PUBLIC_KEY.getKey() : OrderPaymentParameterEnum.CCB_DIGITAL_PUBLIC_KEY.getKey() + ResponseCode.PARAM_NOT_EXIST.getMessage());
                }
            }else{
                throw new BusinessException(payType == 1 ? ResponseCode.PAY_CCB_B2B_PARAM_NOT_EXIST : ResponseCode.PAY_CCB_DIGITAL_PARAM_NOT_EXIST);
            }
        }else{
            logger.error("请求订单服务失败,{}", parameterResult.getMessage());
        }

        return null;
    }
}
